using System;
using ValidationFramework.Reflection;

namespace ValidationFramework.Specifications
{
    public class IsRequiredSpec<T> : SpecificationBase
    {
		private T _InitialValue;

		public T InitialValue
		{
			get
			{
				return _InitialValue;
			}
			set
			{
				if (value != null)
				{
					_InitialValue = value;
					HasInitialValue = true;
				}

			}
		}
        public bool HasInitialValue { get; private set; }
        public bool IsNullable { get; private set; }

        // --- Constructors
        public IsRequiredSpec()
        {
            InitialValue = default(T);
            HasInitialValue = false;
            IsNullable = false;
        }
        public IsRequiredSpec(T initialValue)
        {
            Guard.ArgumentNotNull(initialValue, "initialValue");
            InitialValue = initialValue;
            HasInitialValue = true;
            IsNullable = false;
        }

        // --- Members | Overrides

        /// <inheritdoc />
        public override string DefaultMessage(string tokenizedMemberName, string descriptorType)
        {
            return string.Format("The {0} '{1}' is required.", descriptorType, tokenizedMemberName);
        }

        /// <inheritdoc />
        public override string ToString()
        {
            if (HasInitialValue)
            {
                return string.Format("The value must not be '{0}'", InitialValue);
            }
            else
            {
                return string.Format("The value must not be null");
            }
        }

        /// <inheritdoc />
        public override bool IsSatisfiedBy(object value)
        {
            if (value == null)
                return false;

			if (typeof(T) == typeof(string) && string.IsNullOrEmpty(value as string))
				return false;

            if (HasInitialValue)
            {
                var castedValue = (T)value;
                if (castedValue.Equals(InitialValue))
                    return false;
            }

            return true;
        }

        /// <inheritdoc />
        public override bool CanBeSatisfiedBy(Type typeToCheck)
        {
            if (typeToCheck.IsGenericType && (typeToCheck.GetGenericTypeDefinition().TypeHandle.Equals(TypePointers.NullableTypeHandle)))
            {
                IsNullable = true;
            }
            var valueType = typeof(ValueType);
            if (typeToCheck.BaseType == valueType)
            {
                if (!HasInitialValue && !IsNullable)
                {
                    HasInitialValue = true;
                }
            }
            return true;
        }

        /// <inheritdoc />
        public override bool IsEquivalentTo(ISpecification spec)
        {
            var reqSpecification = spec as IsRequiredSpec<T>;
           
            if (reqSpecification == null)
                return false;
            else
            {
                var bothHaveInitialValue = reqSpecification.HasInitialValue && HasInitialValue;
                var bothNoInitialValue =  !reqSpecification.HasInitialValue && !HasInitialValue;
                if (bothHaveInitialValue)
                    return reqSpecification.InitialValue.Equals(InitialValue);
                else if (bothNoInitialValue)
                    return true;
                else
                    return false;

            }
        }
    }
}
